#include <windows.h>
#include <stdio.h>		//printf used during Install()

#include "lib.h"
#include "cassrvc.h"
#include "ntservmsg.h"
#include "casbase.h"
#include "log.h"

#include "srviface.h"
#include "getrand.h"

#include "opts.h"

#define SERVICE_CONTROL_STOPMSG		(SERVICE_CONTROL_USER + 0)
#define SERVICE_CONTROL_SETLOGLEVEL	(SERVICE_CONTROL_USER + 1)

#define INIFILE	TEXT("server.ini")
#define USRSTAT	TEXT("USERS")


uint32
getrand()
{
	// This function is only called from within a running ServerInterface.
	// This fact implies existence of CNTService::m_pThis and
	// CasinoService::serv.
	//
	return ((CasinoService*)CNTService::m_pThis)->getServer()->rand();
}


CasinoService::CasinoService(const String& ServiceKey, const String& ServiceName,
	int32 DefaultPort)
	: CNTService(ServiceKey, ServiceName), ioIni(NULL)
{
	port = DefaultPort;
	serv = (ServerInterface*)0;

	SetCurrentDirectory();

	SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
	SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlHandler, TRUE);

	Log::init(this);

	// allow multiple instances of casino services
	// adjusts port by a base amount
	// adjust ServiceKey & ServiceName to include a base name
	//
	AdjustCasinoBase();
}

CasinoService::~CasinoService()
{

if (ioIni)
	{
		DeleteCriticalSection(ioIni);
		delete ioIni;
		ioIni = (PCRITICAL_SECTION)0;
	}

    delete serv;

	Log::uninit();
}

void
CasinoService::SetCurrentDirectory()
{
	TCHAR exePath[MAX_PATH + 1];
	int templen;

	templen = (int)GetModuleFileName(NULL, exePath, MAX_PATH);

	while (templen && exePath[templen - 1] != '\\')
		templen--;

	if (templen > 2)
		exePath[templen] = '\0';

	::SetCurrentDirectory((LPTSTR)exePath);
	DEBUGMSG((exePath));
}

void
CasinoService::AdjustCasinoBase()
{
	CasinoBase::load();

	// adjust port by base amount (moves all services to a different range)
	//
	port += CasinoBase::baseport;

	// modify the service names to include the basename (aliases the services
	// which effectively allows the same service to run multiple times...just
	// under different names)
	//
	m_szServiceKey = String("CasinoApp") + CasinoBase::basename + m_szServiceKey;

	m_szServiceName = String("Casino App ") + CasinoBase::basename + String(" ") + m_szServiceName;
}

BOOL
CasinoService::Install(const String& acctname, const String& password)
{
	if (CNTService::Install(acctname, password))
	{
		HKEY hkey;
		DWORD keepAliveTime, dwDisp;
		BOOL valid = FALSE;
            
        //RPF 20090721
        //THE STATUS REGISTRY KEY IS NO LONGER USED TO UPDATE THE COUNT OF PLAYERS
        //NOW WE USE THE INI FILE TO STORE AND UPDATE THE COUNT OF PLAYERS
        //RESET COUNT OF PLAYERS
        //
		// Show keep alive time (with description)
		//

		printf("\nKeep alive time:  ");

		if (ERROR_SUCCESS ==
			::RegCreateKeyExA(HKEY_LOCAL_MACHINE,
				"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0, "",
				REG_OPTION_NON_VOLATILE,
				KEY_ALL_ACCESS, NULL, &hkey, &dwDisp))
		{
			DWORD dwType = REG_DWORD;
			DWORD dwSize = sizeof(int32);

			if (dwDisp == REG_OPENED_EXISTING_KEY)
			{
				if (ERROR_SUCCESS == ::RegQueryValueExA(hkey, "KeepAliveTime",
					NULL, &dwType, (LPBYTE)&keepAliveTime, &dwSize))
					valid = TRUE;
			}
			else
			{
				keepAliveTime = 15*60*1000;		// 15 minute timeout

				if (ERROR_SUCCESS == ::RegSetValueExA(hkey, "KeepAliveTime", 0,
					REG_DWORD, (BYTE*)&keepAliveTime, dwSize))
					valid = TRUE;
			}
			RegCloseKey(hkey);
		}

		if (valid)
		{
			bool8 append = false;

			printf("%d milliseconds", keepAliveTime);

			if (keepAliveTime >= 1000)
			{
				printf(" (");
				if (keepAliveTime >= 60*60*1000)
				{
					printf("%d hours", keepAliveTime / (60*60*1000));
					keepAliveTime %= 60*60*1000;
					append = true;
				}
				if (keepAliveTime >= 60*1000)
				{
					printf("%s%d minutes", append ? ", " : "",
						keepAliveTime / (60*1000));
					keepAliveTime %= 60*1000;
					append = true;
				}
				if (keepAliveTime >= 1000)
				{
					printf("%s%d seconds", append ? ", " : "",
						keepAliveTime / 1000);
					keepAliveTime %= 1000;
					append = true;
				}
				if (keepAliveTime)
					printf("%s%d milliseconds", append ? ", " : "",
						keepAliveTime);

				printf(")");
			}
			printf("\n");
		}
		else
		{
			printf("UNABLE TO DETERMINE: SYSTEM DEFAULT IS 2 HOURS\n");
			printf("\nTo set the keep alive time (globally for all TCP ");
			printf("sockets that use keep alives),\ncreate the following ");
			printf("registry key (type: DWORD, value: milliseconds):\n");
			printf("    HKEY_LOCAL_MACHINE\n        SYSTEM\\");
			printf("CurrentControlSet\\Services\\Tcpip\\");
			printf("Parameters\\KeepAliveTime\n");
		}

		return TRUE;
	}
	return FALSE;
}

BOOL
CasinoService::OnInit()
{
	serv = createServer();

    ioIni = new CRITICAL_SECTION;
	if (ioIni)
		InitializeCriticalSection(ioIni);
	else
	{
		delete ioIni;
		ioIni = (PCRITICAL_SECTION)0;
	}

	if (serv && !serv->initSuccess())
	{
		delete serv;
		serv = (ServerInterface*)0;
	}

	return (BOOL)(serv != (ServerInterface*)0);
}

ServerInterface*
CasinoService::createServer()
{
	return (ServerInterface*)0;
}

void
CasinoService::Run()
{
  	serv->run();
}

BOOL
CasinoService::OnUserControl(DWORD dwOpcode)
{
    switch (dwOpcode)
	{
		case SERVICE_CONTROL_STOPMSG:
			SetStopMsg();
			return TRUE;

		case SERVICE_CONTROL_SETLOGLEVEL:
			SetLogLevel();
			return TRUE;

		default:
			break;
	}
    return FALSE; // not handled
}

void
CasinoService::OnStop()
{
	if (serv && !serv->isStopping())
	{
		serv->stop();
		CNTService::OnStop();
	}
}

void
CasinoService::SetStopMsg()
{
	String szKey;
    HKEY hkey;
	char msg[256];

	// Read the registry parameters
    // Try opening the registry key:
    // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<AppName>\Parameters
	//
	szKey = "SYSTEM\\CurrentControlSet\\Services\\";
	szKey += m_szServiceKey;
	szKey += "\\StopMsg";

    if (ERROR_SUCCESS ==
		RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_QUERY_VALUE, &hkey))
	{
        DWORD dwType = REG_SZ;
        DWORD dwSize = 255;

        if (ERROR_SUCCESS != ::RegQueryValueExA(hkey, "Msg", NULL,
			&dwType, (LPBYTE)msg, &dwSize))
		{
			m_Status.dwWin32ExitCode = GetLastError();
			SetStatus(m_Status.dwCurrentState);
			return;
		}

        RegCloseKey(hkey);
    }
	else
	{
		m_Status.dwWin32ExitCode = GetLastError();
		SetStatus(m_Status.dwCurrentState);
		return;
	}

	if (serv)
		serv->setStopMsg(msg);

    DebugMsg("CasinoService::SetStopMsg(%s)", msg);

	m_Status.dwWin32ExitCode = 0;
	SetStatus(m_Status.dwCurrentState);
}

void
CasinoService::SetLogLevel()
{
	Log::msgWarning("Received log level modification (from ServiceControl)");
	
	uint16 inifile[MAX_PATH + 1];
	int32 len;

	len = GetModuleFileNameW(NULL, inifile, MAX_PATH);
	while (len && inifile[len - 1] != '\\')
		len--;
	wcscpy(inifile + len, INIFILE);
	const String sIniFile = inifile;

	ServerOpts::logAppLevel = (int32)GetPrivateProfileIntW(TEXT("LOG"), TEXT("LogLevel"), (INT)5, sIniFile);

	Log::msgWarning("New log level is %d", ServerOpts::logAppLevel);
}

void
CasinoService::logMsgError(const char* fmt, va_list args)
{
    char buff[2048];
	::wvsprintfA(buff, fmt, args);
    LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_TEXT, buff);
}

void
CasinoService::logMsgInformation(const char* fmt, va_list args)
{
    char buff[2048];
	::wvsprintfA(buff, fmt, args);
    LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_TEXT, buff);
}


void
CasinoService::logMsgWarning(const char* fmt, va_list args)
{
    char buff[2048];
	::wvsprintfA(buff, fmt, args);
    LogEvent(EVENTLOG_WARNING_TYPE, EVMSG_TEXT, buff);
}

void
CasinoService::logMsgError(const wchar_t* fmt, va_list args)
{
    wchar_t buff[2048];
	wvsprintf(buff, fmt, args);
    LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_TEXT, buff);
}

void
CasinoService::logMsgInformation(const wchar_t* fmt, va_list args)
{
    wchar_t buff[2048];
	wvsprintf(buff, fmt, args);
    LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_TEXT, buff);
}


void
CasinoService::logMsgWarning(const wchar_t* fmt, va_list args)
{
    wchar_t buff[2048];
	wvsprintf(buff, fmt, args);
    LogEvent(EVENTLOG_WARNING_TYPE, EVMSG_TEXT, buff);
}

void
CasinoService::logNumUsers(int32 Users,String strAppName)
{
    

    //RPF 20090723 NOW WE ALSO ALLOW TO UPDATE WHEN THE SERVICE IS RUNNING
	if (m_Status.dwCurrentState == SERVICE_STOP_PENDING || m_Status.dwCurrentState ==SERVICE_RUNNING)
	{
        //RPF 20090723
        //THE STATUS REGISTRY KEY IS NO LONGER USED TO UPDATE THE COUNT OF PLAYERS
        //NOW WE USE THE INI FILE TO STORE AND UPDATE THE COUNT OF PLAYERS
        //UPDATE NUMBERS OF PLAYERS PLAYING
        //
        char szNumPlayers[10]; 
        sprintf_s(szNumPlayers,10,"%d",Users);

        // Ensure unique access to ini file
		if (ioIni)
			EnterCriticalSection(ioIni);
		
		try
		{
         CasinoBase::setStrOptEx(strAppName,USRSTAT,szNumPlayers);
        }catch(...)
		{}
		if (ioIni)
			LeaveCriticalSection(ioIni);

	}
}

void
CasinoService::logStoppingCheckpoint()
{
	++m_Status.dwCheckPoint;
	m_Status.dwWaitHint = 30000;
	SetStatus(SERVICE_STOP_PENDING);
}

BOOL
CasinoService::ctrlHandler(DWORD Msg)
{
	if (CNTService::m_pThis)
	{
		switch (Msg)
		{
			case CTRL_C_EVENT:		// ignore these events
			case CTRL_BREAK_EVENT:
			case CTRL_LOGOFF_EVENT:
				break;

			default:
				CNTService::m_pThis->OnStop();
				break;
		}

		return TRUE;
	}
	return FALSE;
}
